package top.maof.book.controller;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import top.maof.book.map.UserMap;
import top.maof.book.model.User;
import top.maof.book.result.Result;
import top.maof.book.service.UserService;
import top.maof.book.utils.JWTUtils;
import top.maof.book.vo.TokenVo;

import javax.mail.internet.MimeMessage;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 登录、退出
 */
@Controller
@ResponseBody
@RequestMapping("/login")
@Slf4j
@Validated
public class LoginController {

    @Autowired
    UserService userService;

    @Autowired
    ValueOperations<String, Object> valueOperations;


    UserMap userMap = UserMap.INSTANCE;


    @GetMapping("/in")
    @ApiOperation("登录")
    public Result<String> login(String username, String password) throws Exception {
        User user = userService.selectByUsername(username);
        if (user != null) {
            Md5Hash md5Hash = new Md5Hash(password, user.getSalt(), 8);
            password = md5Hash.toHex();
            if (user.getPassword().equals(password)) {
                Map<String, String> map = new HashMap<String, String>();
                Long expireTime = System.currentTimeMillis() + JWTUtils.HOUR * 60 * 60 * 1000 - 10 * 60 * 1000;
                map.put("id", String.valueOf(user.getId()));
                map.put("expireTime", expireTime + "");
                String token = JWTUtils.getToken(map);
                return Result.success(token);
            }
        }
        return Result.response(400, "用户名或密码错误");
    }


    @GetMapping("/register")
    @ApiOperation("注册")
    public Result<TokenVo> register(@Valid User user, String code, BindingResult bindingResult) {

        if (userService.selectByUEP(user.getUsername(), user.getEmail(), user.getPhoneNumber()) == null) {
            return Result.response(400, "用户名已被使用 或者该手机号和邮箱号已被注册, 请重新输入!!!");
        }

        String salt = UUID.randomUUID().toString();
        Md5Hash md5Hash = new Md5Hash(user.getPassword(), salt, 8);
        user.setPassword(md5Hash.toHex());
        user.setSalt(salt);
        boolean save = userService.save(user);
        if (save) {
            Map map = new HashMap<String, String>();
            map.put("id", String.valueOf(user.getId()));
            Long expire = System.currentTimeMillis() + JWTUtils.HOUR * 60 * 60 * 1000 - 10 * 60 * 1000;
            map.put("expireTime", expire + "");
            String token = JWTUtils.getToken(map);
            TokenVo tokenVo = new TokenVo();
            tokenVo.setUser(userMap.toUserVo(user));
            tokenVo.setExpireTime(expire);
            tokenVo.setToken(token);
            return Result.success(tokenVo);
        }

        return Result.response(400, "不符合规范");
    }


    @ApiOperation("登录")
    @GetMapping("/newIn")
    public Result<TokenVo> loginIn(String username, String password) throws Exception {
        User user = userService.selectByUsername(username);
        if (user != null) {
            Md5Hash md5Hash = new Md5Hash(password, user.getSalt(), 8);
            password = md5Hash.toHex();
            if (user.getPassword().equals(password)) {
                Map map = new HashMap<String, String>();
                map.put("id", String.valueOf(user.getId()));
                Long expire = System.currentTimeMillis() + JWTUtils.HOUR * 60 * 60 * 1000 - 10 * 60 * 1000;
                map.put("expireTime", expire + "");
                String token = JWTUtils.getToken(map);
                TokenVo tokenVo = new TokenVo();
                tokenVo.setUser(userMap.toUserVo(user));
                tokenVo.setExpireTime(expire);

                tokenVo.setToken(token);
                return Result.success(tokenVo);
            }
        }
        return Result.response(400, "用户名或密码错误");
    }


    @ApiOperation("邮箱登录")
    @GetMapping("/email")
    public Result<TokenVo> emailLogin(String email, String code) throws Exception {
        String keyError = email + "LoginErrorCount";
        String loginErrorCount = valueOperations.get(keyError) + "";
        if (StringUtils.isEmpty(loginErrorCount)) {
            valueOperations.set(keyError, 0, 10, TimeUnit.MINUTES);
        }

        if (Integer.parseInt((String) valueOperations.get(loginErrorCount)) > 5) {
            return Result.response(400, "错误登录次数超过最大值, 请稍后重试!!!");
        }

        User user = userService.getOne(Wrappers.<User>lambdaQuery().eq(!StringUtils.isEmpty(email), User::getEmail, email));
        if (user == null) {
            valueOperations.set(keyError, Integer.parseInt((String) valueOperations.get(loginErrorCount)) + 1);
            return Result.response(404, "邮箱未被注册!!!");
        }

        String hasCode = valueOperations.get(email) + "";

        if (StringUtils.isEmpty(hasCode)) {
            valueOperations.set(keyError, Integer.parseInt((String) valueOperations.get(loginErrorCount)) + 1);
            return Result.response(400, "验证码过期!!!");
        }

        if (!hasCode.equals(code)) {
            valueOperations.set(keyError, Integer.parseInt((String) valueOperations.get(loginErrorCount)) + 1);
            return Result.response(400, "验证码不正确!!!");
        }
        valueOperations.set(keyError, 0);
        valueOperations.set(email, 0, 10, TimeUnit.MINUTES);
        Map map = new HashMap<String, String>();
        map.put("id", String.valueOf(user.getId()));
        Long expire = System.currentTimeMillis() + JWTUtils.HOUR * 60 * 60 * 1000 - 10 * 60 * 1000;
        map.put("expireTime", expire + "");
        String token = JWTUtils.getToken(map);
        TokenVo tokenVo = new TokenVo();
        tokenVo.setUser(userMap.toUserVo(user));
        tokenVo.setExpireTime(expire);
        tokenVo.setToken(token);
        return Result.success(tokenVo);
    }


    @Autowired
    JavaMailSender javaMailSender;


    @Value("${spring.mail.username}")
    private String ownEmail;


    @ApiOperation("获取验证码")
    @GetMapping("/code")
    public Result code(String email) {
        User one = userService.getOne(Wrappers.<User>lambdaQuery().eq(!StringUtils.isEmpty(email), User::getEmail, email));
        if (one == null) {
            return Result.response(404, "邮箱未被注册!!!");
        }
        // 生成验证码
        String time = System.currentTimeMillis() + "";
        String code = time.substring(time.length() - 6);

        SimpleMailMessage mimeMessage = new SimpleMailMessage();
        mimeMessage.setFrom(ownEmail);
        mimeMessage.setTo(email);
        mimeMessage.setSubject("验证码(300秒后过期)");
        mimeMessage.setText(code);
        // 发送验证码
        javaMailSender.send(mimeMessage);
        // 300秒过期
        valueOperations.set(email, code, 300, TimeUnit.SECONDS);
        return Result.success();
    }


    @ApiOperation("刷新token")
    @GetMapping("/refresh")
    public Result<TokenVo> refresh(@RequestHeader("token") String token) throws Exception {
        boolean b = JWTUtils.checkToken(token);
        if (b) {
            Integer userId = JWTUtils.getUserId(token);
            Map map = new HashMap<String, String>();
            map.put("id", userId + "");
            Long expire = System.currentTimeMillis() + JWTUtils.HOUR * 60 * 60 * 1000 - 10 * 60 * 1000;
            map.put("expireTime", expire + "");
            String newToken = JWTUtils.getToken(map);
            TokenVo tokenVo = new TokenVo();
            tokenVo.setExpireTime(expire);
            tokenVo.setToken(newToken);
            return Result.success(tokenVo);
        }
        return Result.response_401();
    }


}
